package cn.maihe.elg.operation.schedule;

import cn.maihe.elg.operation.config.CmbBankConfig;
import cn.maihe.elg.operation.config.SpdBankBiSafeConfig;
import cn.maihe.elg.operation.config.XibBankConfig;
import cn.maihe.elg.operation.config.ZtAccountInfo;
import cn.maihe.elg.operation.model.enums.AcceptOrderStatus;
import cn.maihe.elg.operation.model.enums.CenterNoEnum;
import cn.maihe.elg.operation.model.vo.order.AcceptOrderStatusAuditVO;
import cn.maihe.elg.operation.repository.entity.AcceptOrderInfoDO;
import cn.maihe.elg.operation.repository.entity.AcceptOrderInfoDOCondition;
import cn.maihe.elg.operation.repository.entity.BidCustomerDO;
import cn.maihe.elg.operation.repository.entity.ProjectInfoDO;
import cn.maihe.elg.operation.repository.entity.SpdBankNotice;
import cn.maihe.elg.operation.service.business.BidCustomerService;
import cn.maihe.elg.operation.service.business.ExchangeCenterService;
import cn.maihe.elg.operation.service.business.ProjectInfoService;
import cn.maihe.elg.operation.service.order.AcceptOrderInfoService;
import cn.maihe.elg.operation.service.order.SpdBankNoticeService;
import cn.maihe.elg.operation.utils.CreateGuaranteeUtil;
import cn.maihe.elg.operation.utils.DateUtil;
import cn.maihe.elg.operation.utils.ElgLogger;
import cn.maihe.framework.starter.base.sql.PageBo;
import cn.maihe.framework.starter.mybatis.mapper.PlusEntityWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Date;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @Description 轮询检测受理订单是否已支付
 * @Author WJH
 * @Date 2021/08/10
 */

@Component
@Slf4j
public class CheckAcceptOrderPayStatusTask {

    @Resource
    private AcceptOrderInfoService acceptOrderInfoService;
    @Resource
    private SpdBankNoticeService spdBankNoticeService;
    @Resource
    private ProjectInfoService projectInfoService;
    @Resource
    private BidCustomerService bidCustomerService;
    @Resource
    private SpdBankBiSafeConfig spdBankBiSafeConfig;
    @Resource
    private XibBankConfig xibBankConfig;
    @Resource
    private CmbBankConfig cmbBankConfig;
    @Resource
    private ZtAccountInfo ztAccountInfo;
    @Resource
    private ExchangeCenterService exchangeCenterService;
    private final AtomicBoolean running = new AtomicBoolean(false);

    @Value("${cn.maihe.elg.operation.schedule.payStatusCheckTimes:1000}")
    private int checkTimes;

    @Value("${cn.maihe.elg.operation.schedule.payStatusCheckDateInterval:15}")
    private int payStatusCheckDateInterval;

    @Value("${cn.maihe.elg.auto-guarantee-status:6}")
    private Integer [] autoGuaranteeStatus;

    public CheckAcceptOrderPayStatusTask() {

    }

    @Scheduled(cron = "${cn.maihe.elg.operation.schedule.checkPayStatusCron}")
    public void NoticeRetry() {
        try {
            if (!"1".equals(spdBankBiSafeConfig.getOpenFlag()) && !"1".equals(xibBankConfig.getOpenFlag()) && !"1".equals(cmbBankConfig.getOpenFlag())) {
                log.info("当前已关闭银企直连功能，暂无需开启检测订单支付状态任务");
                return;
            }

            if (running.compareAndSet(false, true)) {
                log.info("检测订单支付状态任务开始----");
                PlusEntityWrapper<AcceptOrderInfoDO> condition = AcceptOrderInfoDOCondition.builder()
                        .andCheckTimesLe(checkTimes)
                        .and().orStatusEq(AcceptOrderStatus.WAIT_PAY_CONFIRM.getStatus()).orStatusEq(AcceptOrderStatus.WAIT_PAY.getStatus())
                        .and().orNextTimeLt(new Date()).orNextTimeIsNull().build();
                PageBo<AcceptOrderInfoDO> page = acceptOrderInfoService.selectPageBo(condition, 1, 500);
                if (page != null && CollectionUtils.isNotEmpty(page.getList())) {
                    CompletableFuture[] cfs = page.getList().stream()
                            .map(this::doCheckPayStatus).toArray(CompletableFuture[]::new);

                    //等待全部任务结束
                    CompletableFuture.allOf(cfs).join();
                }
                log.info("检测订单支付状态任务结束，共检测订单记录数[{}]", page.getTotal());
            } else {
                log.info("检测订单支付状态任务已开启执行中，等待其结束后重试");
            }
        } catch (Exception e) {
            log.error("检测订单支付状态任务结束：{}", e.getMessage(), e);
        } finally {
            running.compareAndSet(true, false);
        }

    }

    private CompletableFuture<SpdBankNotice> doCheckPayStatus(AcceptOrderInfoDO orderInfo) {
        final AcceptOrderStatus orderStatus = AcceptOrderStatus.resolve(orderInfo.getStatus());
        final String acceptOrderNo = orderInfo.getAcceptOrderNo();

        return CompletableFuture.supplyAsync(() -> {
            CenterNoEnum centerNoEnum = exchangeCenterService.getCenterNoEnumById(orderInfo.getCenterId());
            if(AcceptOrderStatus.WAIT_PAY == orderStatus){
                if(!Arrays.asList(autoGuaranteeStatus).contains(orderStatus)){
                    if(!CreateGuaranteeUtil.isGuoGu(centerNoEnum)){
                        return null;
                    }
                }
            }

            BidCustomerDO bidCustomer = bidCustomerService.selectById(orderInfo.getBidCustomerId());
            ProjectInfoDO projectInfo = projectInfoService.selectById(orderInfo.getProjectId());
            String payerAcctNo = bidCustomer.getBankCardNum().replace(" ", "");
            String payeeAcctNo = ztAccountInfo.getAccNo();
            Long guaranteeFee = orderInfo.getGuaranteeFee();
//            String sectionCode = projectInfo.getSectionCode();
            String projectCode = projectInfo.getProjectCode();
            SpdBankNotice spdBankNotice = spdBankNoticeService.selectMatchPayInfoAndSectionCode(payerAcctNo, payeeAcctNo, guaranteeFee, projectCode, orderInfo);
            if (spdBankNotice != null && projectInfo.getTemplateId() != null) {
                try {
                    orderInfo.setTemplateId(projectInfo.getTemplateId());
                    spdBankNotice = doConfirmPayStatus(orderInfo, spdBankNotice);
                } catch (Exception e) {
                    setNextTimeCheck(orderInfo);
                    throw new RuntimeException("自动确认已转账处理异常:" + e.getMessage(), e);
                }
            } else {
                setNextTimeCheck(orderInfo);
            }
            return spdBankNotice;
        }).whenCompleteAsync((spdBankNotice, t) -> {
            if (t != null) {
                ElgLogger.error(log, l -> l.error("受理订单号[{}] >> 异步检测订单支付状态异常:{}", acceptOrderNo, t.getMessage(), t));
            } else {
                if (spdBankNotice != null && orderInfo.getAcceptOrderNo().equals(spdBankNotice.getAcctNo())) {
                    ElgLogger.info(log, l -> l.info("受理订单号[{}] >> 异步检测订单支付状态已支付[{}:{}]", acceptOrderNo, spdBankNotice.getId(), spdBankNotice.getCoreSerNo()));
                }
            }
        });
    }

    private void setNextTimeCheck(AcceptOrderInfoDO orderInfo) {
        try {
            AcceptOrderInfoDO update = new AcceptOrderInfoDO();
            update.setId(orderInfo.getId());
            int checkTimes = orderInfo.getCheckTimes() == null ? 1 : orderInfo.getCheckTimes() + 1;
            update.setCheckTimes(checkTimes);
            if(checkTimes>10){
                update.setNextTime(Date.from(Instant.now().plus(payStatusCheckDateInterval, ChronoUnit.MINUTES)));
            }else {
                update.setNextTime(Date.from(Instant.now().plus(checkTimes * 2 - 1, ChronoUnit.MINUTES)));
            }
            acceptOrderInfoService.updateById(update);
        } catch (Exception e) {
            log.error("受理订单号:{}更新设置下次检测支付状态异常：{}", orderInfo.getAcceptOrderNo(), e.getMessage(), e);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public SpdBankNotice doConfirmPayStatus(AcceptOrderInfoDO orderInfo, SpdBankNotice spdBankNotice) {

        AcceptOrderStatusAuditVO auditVO = new AcceptOrderStatusAuditVO();
        auditVO.setId(orderInfo.getId());
        auditVO.setStatus(orderInfo.getStatus());
        auditVO.setAuditStatus(1);
        auditVO.setPayTime(DateUtil.formatDateStr(spdBankNotice.getTransDate(), DateUtil.FORMAT_TRADEDATE, DateUtil.FORMAT_DATE));
        auditVO.setTemplateId(orderInfo.getTemplateId());
        auditVO.setAuditRemark("系统检测到转账已到账，自动审核通过");
        ElgLogger.info(log, l -> l.info("核心交易流水号:{} << 母账户账号[{}],来账账号[{}],交易金额[{}元],附言[{}] 检测识别为受理订单号[{}]保函编号[{}]的转账记录并做自动审核通过",
                spdBankNotice.getCoreSerNo(), spdBankNotice.getParentAcctNo(), spdBankNotice.getAcctNo(), spdBankNotice.getTransAmt(),
                spdBankNotice.getAttach(), orderInfo.getAcceptOrderNo(), orderInfo.getGuaranteeNo()));

        //自动审核
        acceptOrderInfoService.auditWaitPayConfirm(auditVO);

        //更新入金通知记录
        spdBankNotice.setAcceptOrderNo(orderInfo.getAcceptOrderNo());
        spdBankNoticeService.updateById(spdBankNotice);
        return spdBankNotice;
    }


}
